package features;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import classifiers.helper.FeatureExtractionMethod;

import features.word_counter.WordCounter;

import utils.LineRegex;
import utils.Pair;
import utils.TagClustering;
import utils.Utils;
import weka.core.Attribute;
import weka.core.DenseInstance;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.SparseInstance;
import weka.core.converters.ArffSaver;
import weka.core.converters.Saver;


public abstract class FeatureExtractor {
	
	public enum InstanceType { DENSE,SPARSE }
	
	private InstanceType instance_type = InstanceType.SPARSE;
	private LabelExtractor label_extractor;
	
	protected WordCounter wc;
	protected FeatureExtractionMethod method;
	
	
	public FeatureExtractor (WordCounter wc, FeatureExtractionMethod method) {
		this.method = method;
		this.wc = wc;
	}
	
	public LabelExtractor getLabel_extractor() {
		return label_extractor;
	}

	public void setLabel_extractor(LabelExtractor label_extractor) {
		this.label_extractor = label_extractor;
	}

	public void extractFeatures( String in_file , String out_directory_name ) throws Exception {
		if ( label_extractor == null ) throw new Exception("LABEL EXTRACTOR NOT SET");
		loadExtra(in_file);
		Instances dataset = getDataset(getDatasetName(in_file));
		ArffSaver saver = new ArffSaver();
		saver.setRetrieval(Saver.INCREMENTAL);
		saver.setStructure(dataset);	
		saver.setFile(new File(out_directory_name+getArffFileForInputFile(in_file)));
		BufferedReader jin = new BufferedReader(new InputStreamReader(new FileInputStream(in_file)));
		while ( jin.ready() ) {
			String line = jin.readLine();
			List<Pair<Double,Integer>> vals_idxs = getFeatures(line);
			vals_idxs.addAll(label_extractor.getClassFeatures(line,getNumFeatures()));
			saver.writeIncremental(getInstance(getWeightBasedOnScore(line),vals_idxs,instance_type,dataset.numAttributes()));
		}
		saver.writeIncremental(null);
		jin.close();
	}
	
	private double getWeightBasedOnScore(String line) {
		double score = LineRegex.getScore(line);
		score = score<0?0:score;
//		return Math.log(score+2);
		return 1.0;
	}

	private String getArffFileForInputFile(String in_file) {
		in_file = Utils.getShortFilename(in_file);
		in_file = in_file.substring(0, in_file.length()-4);
		in_file += "_"+ getMethod()+".arff";
		return in_file;
	}

	private Instance getInstance(double weight,List<Pair<Double, Integer>> vals_idxs,
			InstanceType instance_type, int numAttributes) {
		if ( instance_type == InstanceType.SPARSE) {
			Collections.sort(vals_idxs,new Comparator<Pair<Double,Integer>>() {
	
				@Override
				public int compare(Pair<Double, Integer> a,
						Pair<Double, Integer> b) {
					return a.b-b.b;
				}
			});
			double vals[] = new double[vals_idxs.size()];
			int idxs[] = new int[vals_idxs.size()];
			int k = 0;
			for ( Pair<Double, Integer> val_idx : vals_idxs ) {
				vals[k] = val_idx.a;
				idxs[k] = val_idx.b;
				++k;
			}
			return new SparseInstance(weight, vals, idxs, numAttributes);
		}
		else {
			double values[] = new double[numAttributes];
			for ( Pair<Double, Integer> val_idx : vals_idxs ) {
				values[val_idx.b] = val_idx.a;
			}
			return new DenseInstance(weight,values);
		}
	}

	private String getDatasetName(String in_file) {
		int num_instances = Utils.getNumInstances(in_file);
		return num_instances+"_"+method;
	}

	protected abstract int getNumAttributes();
	
	protected int getNumFeatures() {
		return wc.getNumWords();
	}
		
	protected List<Attribute> getClassAttributes() {
		return label_extractor.getClassAttributes();
	}
	
	protected abstract List<Pair<Double,Integer>> getFeatures(String line) throws Exception;

	private Instances getDataset(String name) {
		ArrayList<Attribute> attributes = getAttributes();
		attributes.addAll(getClassAttributes());
		return new Instances(name,attributes,1000000);
	}

	protected abstract ArrayList<Attribute> getAttributes();

	protected HashMap<String,Double> getWordCount(String line) {
		return wc.getWordCount(line);
	}
	
	protected void loadExtra(String in_file) throws Exception {
		//DO NTH
	}
	
	public FeatureExtractionMethod getMethod() {
		return method;
	}

	public InstanceType getInstance_type() {
		return instance_type;
	}

	public void setInstance_type(InstanceType instance_type) {
		this.instance_type = instance_type;
	}

	public static FeatureExtractor getFeatureExtractor(WordCounter wc,
			FeatureExtractionMethod method) {
		if ( method == FeatureExtractionMethod.nom ) {
			return new NominalFeatureExtractor(wc,method);
		}
		else {
			return new WordCountFeatureExtractor(wc,method);
		}
	}

	public FeatureExtractor copy() {
		return getFeatureExtractor(wc, method);
	}
	
}


